<?php

namespace app\common\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\Exception;
use tmcore\utils\Helper;
use think\facade\Config;
use tmcore\services\addons\TerminalService;
use app\admin\service\AdminTokenService;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
use Workerman\Protocols\Http\ServerSentEvents;
use Workerman\Worker;

class Terminal extends Command
{
    /**
     * @var array
     */
    protected $config = [];

    /**
     * @var Worker
     */
    protected $workerServer;

    /**
     * 指令配置
     */
    protected function configure()
    {
        $this->setName('terminal')
            ->addArgument('action', Argument::REQUIRED, "start|stop|restart|reload|status|connections")
            ->addOption('daemon', 'd', Option::VALUE_NONE, 'Run Terminal server in daemon mode.')
            ->setDescription('Terminal server');
    }

    /**
     * 初始化参数
     * @param Input $input
     * @param Output $output
     * @return void
     */
    private function init(Input $input, Output $output)
    {
        $action = $input->getArgument('action') ?: 'start';
        if (!in_array($action, ['start', 'stop', 'restart', 'reload', 'status', 'connections'])) {
            $output->writeln("<error>Invalid argument action:$action, Expected start|stop|restart|reload|status|connections .</error>");
            exit(1);
        }

        global $argv;
        $argv[1] = $action;
        if ($input->hasOption('d')) {
            $argv[2] = '-d';
        } else {
            unset($argv[2]);
        }

        $this->config = config('workerman.terminal');

        $this->config['option']['pidFile'] = runtime_path() . 'workerman/server.pid';
        Helper::check_mkdir($this->config['option']['pidFile']);

        $this->config['option']['logFile'] = runtime_path() . 'workerman/log/' . date('Y-m-d') . '.log';
        Helper::check_mkdir($this->config['option']['logFile']);
    }

    /**
     * 执行指令
     * @param Input $input
     * @param Output $output
     * @return void
     */
    protected function execute(Input $input, Output $output)
    {
        $this->init($input, $output);

        Worker::$pidFile = $this->config['option']['pidFile'];
        Worker::$logFile = $this->config['option']['logFile'];

        // 创建 http 服务
        $this->workerServer = new Worker($this->config['protocol'] . '://' . $this->config['ip'] . ':' . $this->config['port'], []);
        $this->workerServer->count = $this->config['count'];

        $this->workerServer->onMessage = function(TcpConnection $connection, Request $request)
        {
            // 监听SSE请求，执行终端命令
            if ($request->header('accept') === 'text/event-stream') {
                $params = $request->get();

                $token = $params['token'] ?? '';
                if(!$this->check_login($token, $connection)) return;

                $terminalService = TerminalService::instance();
                $terminalService
                    ->setConnection($connection)
                    ->setConnectionParams($params)
                    ->execute();
                return;
            }
            $connection->send('ok');
        };

        try {
            cache('workerman_terminal_status', 1);
            Worker::runAll();
        } catch (\Exception $e) {
            $output->warning($e->getMessage());
        }
    }

    /**
     * 检验登录
     * @param $token
     * @param TcpConnection $connection
     * @return bool
     */
    private function check_login($token, TcpConnection $connection)
    {
        try {
            if (empty($token)) {
                throw new Exception('请求参数缺少token');
            }

            // token验证
            $adminInfo = AdminTokenService::getAdminInfo($token);
            if (empty($adminInfo)) {
                throw new Exception('token授权失败');
            }
            return true;
        } catch (\Exception $e){
            $sys_header = Config::get('cookie.header');
            $headers = array_merge($sys_header ?? [], [
                'X-Accel-Buffering' => 'no',
                'Content-Type' => 'text/event-stream',
                'Cache-Control' => 'no-cache',
            ]);
            $connection->send(new Response(200, $headers, "\r\n"));
            $connection->send(new ServerSentEvents(['event' => 'message', 'data' => 'Error:登录失败,错误信息:' . $e->getMessage()]));
            return false;
        }
    }
}